Skip to content

Conversation

@mmishra100
Copy link

This change enables EROFS filesystems to be specified as regular mounts in the OCI bundle config.json, not just as rootfs or debug mounts.

EROFS mounts are now tracked in goferMountConfs alongside lisafs mounts. The implementation adds:

  • IsErofsMount() helper in specutils to identify EROFS mounts
  • Updated mount index tracking across container.go, gofer.go, and vfs.go
  • Support for opening EROFS image files and passing FDs to the sandbox
  • EROFS case in getMountNameAndOptions() for proper mount setup

Key implementation details:

  • EROFS mounts are included in goferMountConfs but skip gofer-specific processing (e.g., lisafs serving, filestore creation)
  • Mount type determination happens before mount hint logic
  • Proper index tracking ensures EROFS mounts increment indices but skip lisafs-only operations

Fixes #12307

This change enables EROFS filesystems to be specified as regular mounts
in the OCI bundle config.json, not just as rootfs or debug mounts.

EROFS mounts are now tracked in goferMountConfs alongside lisafs mounts.
The implementation adds:
- IsErofsMount() helper in specutils to identify EROFS mounts
- Updated mount index tracking across container.go, gofer.go, and vfs.go
- Support for opening EROFS image files and passing FDs to the sandbox
- EROFS case in getMountNameAndOptions() for proper mount setup

Key implementation details:
- EROFS mounts are included in goferMountConfs but skip gofer-specific
  processing (e.g., lisafs serving, filestore creation)
- Mount type determination happens before mount hint logic
- Proper index tracking ensures EROFS mounts increment indices but skip
  lisafs-only operations

Fixes google#12307
@google-cla
Copy link

google-cla bot commented Nov 28, 2025

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@mmishra100 mmishra100 mentioned this pull request Nov 28, 2025
@stepancheg
Copy link
Contributor

Hello, gVisor devs! Can you review this PR please?

@ayushr2
Copy link
Collaborator

ayushr2 commented Jan 8, 2026

Thanks for the PR @stepancheg, will have a look this week.

Copy link
Collaborator

@ayushr2 ayushr2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add tests to runsc/container:container_test. We already have EROFS tests for debug mounts and rootfs:

// createImageEROFS creates the EROFS image from the source directory using the requested options.

Comment on lines +507 to +510
func IsErofsMount(m specs.Mount) bool {
return m.Type == erofs.Name
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think specutils should take a dependency on fsimpl/erofs for this == "erofs" check. specutils has very limited dependencies on the sentry. erofs pulls in a lot of stuff.

Similar to the duplication of the string "bind" above on line 503 and in

Bind = "bind"
, we should just have m.Type == "erofs" and live with the duplication. It's not a big deal. In the future maybe we can move all these filesystem constants to a smaller high level package and refactor.

Comment on lines 817 to +819
if info.goferMountConf.ShouldUseLisafs() {
info.goferFD = c.goferFDs.removeAsFD()
} else if info.goferMountConf.ShouldUseErofs() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use specutils.HasMountConf instead

Comment on lines +536 to +540
// EROFS mounts are in goferMountConfs but gofer doesn't set them up
if specutils.IsErofsMount(m) {
mountIdx++
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use specutils.HasMountConfig instead

Comment on lines +703 to +708
// EROFS mounts are in goferMountConfs but gofer doesn't resolve them
if specutils.IsErofsMount(m) {
cleanMounts = append(cleanMounts, m)
mountIdx++
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use specutils.HasMountConfig instead

Comment on lines +345 to 352
// EROFS mounts are in goferMountConfs but gofer doesn't serve them
if specutils.IsErofsMount(m) {
mountIdx++
continue
}
if !specutils.IsGoferMount(m) {
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what we really need is specutils.HasMountConfig(m).

// HasMountConfig returns true if the given mount has an associated GoferMountConf.
func HasMountConfig(m specs.Mount) bool {
	return IsGoferMount(m) || IsErofsMount(m)
}

The logic below handles the rest correctly by continuing if !mountConf.ShouldUseLisafs()

Comment on lines +914 to 921
// EROFS mounts are in goferMountConfs but don't have self-backed filestores
if specutils.IsErofsMount(c.Spec.Mounts[i]) {
goferMntIdx++
continue
}
if !specutils.IsGoferMount(c.Spec.Mounts[i]) {
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use specutils.HasMountConfig instead

// Handle bind mounts.
for i := range c.Spec.Mounts {
if !specutils.IsGoferMount(c.Spec.Mounts[i]) {
if !specutils.IsGoferMount(c.Spec.Mounts[i]) && !specutils.IsErofsMount(c.Spec.Mounts[i]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use !specutils.HasMountConfig() instead

}
c.GoferMountConfs = append(c.GoferMountConfs, goferConf)

// Handle bind mounts.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update comment to say "Handle sub mounts."

EROFS mounts are not bind mounts.

Comment on lines +1050 to +1054
// EROFS mounts are in goferMountConfs but don't need filestore processing
if specutils.IsErofsMount(m) {
mountIdx++
continue
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this. In c.createGoferFilestore(), if !goferConf.IsFilestorePresent(), then we return immediately.

Moreover, in the future, we may want to support EROFS+overlay, for which we will need to create filestore for the upper layer.

Comment on lines +1398 to +1406
// Align spec mount with gofer mount conf by skipping non-gofer/non-erofs mounts
// Skip alignment for root mount (i == 0) because it is not present in spec.Mounts
if i > 0 {
for mountIdx < len(c.Spec.Mounts) &&
!specutils.IsGoferMount(c.Spec.Mounts[mountIdx]) &&
!specutils.IsErofsMount(c.Spec.Mounts[mountIdx]) {
mountIdx++
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all these changes here are really complex and hard to follow.

The c.GoferMountConfs list is a subset of c.Spec.Mounts. It is difficult to iterate configs and track mount index. Everywhere else (as you can see in this PR), we iterate the mounts and track the config index. That approach also requires handling rootfs separately (outside the main loop), but that should be OK:

  • rootfs often requires special handling, as you can see below, we need to handle rootfs differently in the if i==0 branch.
  • It is consistent with the rest of the codebase.

So it should be something like:

switch {
    case c.GoferMountConfs[0].ShouldUseLisafs():
			refactored code to create socket pair and donate the gofer end and append the sandbox end
    case c.GoferMountConfs[0].ShouldUseErofs():
			f, err := os.Open(rootfsHint.Mount.Source)
			if err != nil {
				return nil, nil, nil, nil, fmt.Errorf("opening rootfs image %q: %v", rootfsHint.Mount.Source, err)
			}
			sandEnds = append(sandEnds, f)
}

cfgIdx := 1
for _, m := c.Spec.Mounts {
  if !specutils.HasMountConfig(m) {
    continue
  }
  switch {
    case c.GoferMountConfs[cfgIdx].ShouldUseLisafs():
			refactored code to create socket pair and donate the gofer end and append the sandbox end
    case c.GoferMountConfs[cfgIdx].ShouldUseErofs():
			f, err := os.Open(m.Source)
			if err != nil {
				return nil, nil, nil, nil, fmt.Errorf("opening EROFS image %q: %v", m.Source, err)
			}
			sandEnds = append(sandEnds, f)
  }
  cfgIdx++
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Erofs as mount

3 participants